<script lang="ts">
import { Teleport, createVNode } from 'vue';
import Mask from '../mask/index.vue';
import Content from '../content/index.vue';
import { renderSomeContent, canUseDocElement, toPx } from '../utils';
import { ElButton } from 'element-plus';
import QLoading from '@/components/q-loading/index.vue';

type MousePosition = { x: number; y: number } | null;

let zIndex = 3000;
let mousePosition: MousePosition;

/**
 * 获取鼠标位置
 */
const getClickPosition = (e: MouseEvent) => {
    mousePosition = {
        x: e.pageX,
        y: e.pageY
    };
};

/**
 * 只有点击事件支持从鼠标位置动画展开
 */
if (canUseDocElement && canUseDocElement()) {
    useEventListener(document.documentElement, 'click', getClickPosition, true);
}

export default {
    name: 'QModal',
    inheritAttrs: false,
    props: {
        width: {
            type: String,
            default: '520px'
        },

        height: {
            type: String
        },
        visible: {
            type: Boolean,
            default: false
        },
        type: {
            type: String,
            default: 'info'
        },
        closable: {
            type: Boolean,
            default: false
        },
        centered: {
            type: Boolean,
            default: false
        },
        title: {
            type: String,
            default: '提示'
        },
        bodyHeight: {
            type: [String, Number],
            default: 'auto'
        },
        wrapClassName: {
            type: String,
            default: ''
        },
        footer: {
            type: [Boolean, String],
            default: true
        },
        close: {
            type: Function,
            default: () => {}
        },
        fullscreen: {
            type: Boolean,
            default: false
        },
        footerCenter: {
            type: Boolean,
            default: false
        },
        // 隐藏取消按钮，不展示
        hideCancelBtn: {
            type: Boolean,
            default: false
        },
        // 隐藏确定按钮，不展示
        hideOkBtn: {
            type: Boolean,
            default: false
        },
        // 关闭取消按钮关闭 dialog
        closeCancel: {
            type: Boolean,
            default: false
        },
        afterClose: {
            type: Function,
            default: () => {}
        },
        okText: {
            type: [String, Function],
            default: '确定'
        },
        cancelText: {
            type: [String, Function],
            default: '取消'
        },
        okType: {
            type: String,
            default: 'primary'
        },
        confirmLoading: {
            type: Boolean,
            default: false
        },
        okButtonProps: {
            type: Object,
            default: () => ({})
        },
        cancelButtonProps: {
            type: Object,
            default: () => ({})
        }
    },
    emits: ['update:visible', 'cancel', 'ok', 'fullscreen'],
    setup(props, { attrs, emit, slots }) {
        const dialogRef = ref();
        const animatedVisible = ref(props.visible); // 控制动画变量
        const isScrollLocked = useScrollLock(document.body);

        const renderFooter = () => {
            const {
                okText = slots.okText?.(),
                okType = 'primary',
                cancelText = slots.cancelText?.(),
                confirmLoading,
                okButtonProps,
                cancelButtonProps,
                hideCancelBtn,
                hideOkBtn
            } = props;

            return [
                hideCancelBtn
                    ? ''
                    : createVNode(
                          ElButton,
                          {
                              size: 'default',
                              onClick: handleCancel,
                              ...cancelButtonProps
                          },
                          {
                              default: () => renderSomeContent(cancelText)
                          }
                      ),
                hideOkBtn
                    ? ''
                    : createVNode(
                          ElButton,
                          {
                              size: 'default',
                              type: okType,
                              loading: confirmLoading,
                              onClick: handleOk,
                              ...okButtonProps
                          },
                          {
                              default: () => renderSomeContent(okText),
                              loading: () => createVNode(QLoading)
                          }
                      )
            ];
        };

        watch(
            () => props.visible,
            () => {
                if (props.visible) {
                    animatedVisible.value = true;
                    isScrollLocked.value = true;
                    zIndex++;
                } else {
                    isScrollLocked.value = false;
                }
            },
            { flush: 'post', immediate: true } // DOM 更新后获取
        );

        const handleOk = (e: MouseEvent) => {
            emit('ok', e);
        };

        const handleCancel = (e: MouseEvent) => {
            if (!props.closeCancel) {
                emit('update:visible', false);
            }
            emit('cancel', e);
        };

        const handleFullscreen = (expand: boolean) => {
            emit('fullscreen', expand);
        };

        const onVisibleChange = (newVisible: boolean) => {
            if (newVisible) {
                return;
            } else {
                props.afterClose?.();
                animatedVisible.value = false;
                emit('fullscreen', false);
            }
        };

        return () =>
            h(Teleport, { to: 'body' }, [
                animatedVisible.value
                    ? h(
                          'div',
                          {
                              ref: dialogRef.value,
                              class: 'm-dialog'
                          },
                          [
                              h(Mask, {
                                  visible: props.visible,
                                  zIndex
                              }),
                              h(
                                  'div',
                                  {
                                      class: [
                                          'm-dialog__wrap',
                                          props.wrapClassName,
                                          { 'm-dialog__centered': props.centered }
                                      ],
                                      id: `m-dialog__${zIndex}`,
                                      style: { 'z-index': zIndex + 1 }
                                  },
                                  h(
                                      Content,
                                      {
                                          ...props,
                                          ...attrs,
                                          bodyHeight: toPx(props.bodyHeight),
                                          mousePosition,
                                          visibleChange: onVisibleChange,
                                          onCancel: handleCancel,
                                          onFullscreen: handleFullscreen,
                                          zIndex
                                      },
                                      {
                                          header: () => slots.header?.() || props.title,
                                          default: () => slots.default?.(),
                                          footer: () => slots.footer?.() || renderFooter()
                                      }
                                  )
                              )
                          ]
                      )
                    : null
            ]);
    }
};
</script>

<style lang="scss" scoped>
@include b(m-dialog) {
    position: relative;
    z-index: 2000;

    @include e(wrap) {
        position: fixed;
        inset: 0;
        width: 100%;
        overflow: auto;

        @include scrollbar_y;
        @include scrollbar_x(8px);
        @include scrollbar_hover;
    }

    @include e(centered) {
        text-align: center;

        @include pseudo(before) {
            display: inline-block;
            width: 0;
            height: 100%;
            vertical-align: middle;
            content: '';
        }

        @include b(m-modal) {
            top: 0;
            display: inline-block;
            padding: 0;
            text-align: left;
            vertical-align: middle;
        }
    }
}
</style>
